//	File : TcWiz2.Cpp
//	Implementation of the Translate Block function.
//	Implementation file of TcWizard.h
//
#include <dos.h>
#include <stdio.h>
#include <assert.h>
#include <alloc.h>
#include <conio.h>
#include "Disc.h"
#include "TcWizard.h"
#include "Disasm.h"
/*----------------------------------------------------------------------
Wrapper Fn. to find Modified Registers. Only used by IsTernaryOperatorCode.
----------------------------------------------------------------------*/
void TcWizard::FindRegsModified(Byte far *BlockBegin,Sword BlockSize,Byte *RegModified)
{
  TranslateBlock(BlockBegin,BlockSize,0,NULL,RegModified,NULL);
}
/*----------------------------------------------------------------------
Wrapper Fn. to find 'Used' registers. Only used by IsTernaryOperatorCode.
----------------------------------------------------------------------*/
void TcWizard::FindRegsUsed(Byte far *BlockBegin,Sword BlockSize,Byte *RegModified,Byte *RegUsed)
{
  TranslateBlock(BlockBegin,BlockSize,0,NULL,RegModified,RegUsed);
}
/*----------------------------------------------------------------------
Overloaded Fn. which ignores RegFlags parameter, i.e. not checking which
registers are modified in the code block.
----------------------------------------------------------------------*/
void TcWizard::TranslateBlock(Byte far *BlockBegin,Sword BlockSize,int How,StringList *sList)
{
  TranslateBlock(BlockBegin,BlockSize,How,sList,NULL,NULL);
}
/*
[-----------------------------------------------------------------------]
[ TranslateBlock							]
[-----------------------------------------------------------------------]
[ Converts the given block of code into C-code.				]
[									]
[ BlockBegin - Starting of the code block				]
[ BlockEnd   - End of the code fragment					]
[ How	     - How to store in the StringList				]
[		- as SEPARATE_LINES or COMMA_EXPRESSIONS		]
[ sList      - StringList in which the output lines are stored		]
[ RegModified- Array of flags to indicate which registers were		]
[		modified in the given code fragment			]
[ RegUsed    - Array of flags to indicate which registers are 'used'	]
[		i.e. accessed.						]
[		(Elements of both RegModified & RegUsed are set to 1 by	]
[		 this procedure, and this proc.does not make use of	]
[		 these parameters for anything else.)			]
[-----------------------------------------------------------------------]
*/
void TcWizard::TranslateBlock(
	Byte far *BlockBegin,
	Sword BlockSize,
	int How,
	StringList *sList,
	Byte *RegModified,
	Byte *RegUsed
	)
{
#define marknotused(Reg) NotUsed[Reg]=1;
#define markused(Reg) NotUsed[Reg]=0;
#define CheckAndOutput(str,how,slist) \
	if (!RegModified && !RegUsed) Output(str,how,slist)
#define CheckAndDeletePending(Reg) \
	if (!RegModified && !RegUsed) DeletePending(Reg)
#define CheckAndAddPending(str) \
	if (!RegModified && !RegUsed) AddPending(str)
#define SetRegModified(Reg) \
	if (RegUsed && !RegUsed[Reg]) RegModified[Reg]=1; \
	marknotused(Reg)
#define SetRegModified2(Reg) \
	if (RegUsed && !RegUsed[Reg]) RegModified[Reg]=2; \
	marknotused(Reg)
#define SetRegUsed(Reg)     \
	if (RegUsed) RegUsed[Reg]=1; \
	markused(Reg)
#define CheckAndSetRegUsed(Oper,Data) \
	if(Oper==REG_DIRECT && !RegisterVariable(Data)) SetRegUsed(Data);
  Byte NotUsed[8+8+4];
  memset(NotUsed,0,sizeof(NotUsed));

  if (BlockSize<=0) return;
  int InstLen;
  int val,i,oper;
  int row=wherey()+1;
  SwitchInfo SwInfo;	// Structure used for SWITCH statements.
  SwInfo.NumClauses=0;
  String s_LineBuffer;
  char *LineBuffer = (char *)s_LineBuffer;
  Byte far *Inst;
  Disasm d;
  d.SetCodePtr(CodePtr);
  Inst = BlockBegin;
  Dword ctr = 0;
  String tmp,tmp1,toStr;
  #define toString(str) toStr.Copy(str)
  InstInfo inst,previnst;
  char PrevImmValueStored=0;
  Word PrevImmediateValue;		// Used in converting 'long-int' code.
  Variable v1,v2;
  int v2Type;
  char *vName;
  while(ctr<BlockSize && Inst<ProcEndAddress)
  {
    // Set the data segment for the next instruction as the usual data segment
    // if the previous instruction was not a segment override.
    Byte far *DataSegment;
    if (inst.Instr!=SEGMENT)
      DataSegment = GetDataSegment();
    DataSegment = DataSegment;

    InstAddr = FP_OFF(Inst);
    gotoxy(5,row); printf("Decoding Instruction at address % 4X\r",InstAddr);
    d.TraceInst(Inst,InstLen);
    inst = d.CurInst;
    TranslateTrickyInsts(inst);
    LineBuffer[0] = 0;
    // Are we moving the return value of any procedure (even after some
    //  arithmetic operation) to somewhere? If so,  delete the corresponding
    //  pending o/p line. But Make sure that the 2nd operand is a register
    if (inst.Operand2 == REG_DIRECT)
      CheckAndDeletePending(Regs[inst.Data21]);
    // Is it a single operand instruction?
    if (inst.Operand1 == REG_DIRECT && inst.Operand2==OPER_UNKNOWN)
      CheckAndDeletePending(Regs[inst.Data11]);
    // Is the current address given a label in the code?
    // If so, print the label.
    if (GetLabel(InstAddr,tmp))
    {
      tmp += ":";
      CheckAndOutput(tmp,How,sList);
    }
    // If this is a SWITCH statement, make necessary arrangements.
    if (IsSwitchStatement(InstAddr))
      FillSwitchInfo(Inst,inst,SwInfo);

    // Find the datatype of the memory operand used in this instruction
    // (if any) and register it in the LocalVars/GlobalVars list.
    if (inst.Instr!=LEA)	// since argument to LEA can be any datatype.
      FindAndRegisterDataTypes(inst);

    tmp1 = "";
    // Now trace the instruction.
    switch(inst.Instr)
    {
      // Segment override.
      case SEGMENT: Word Segment = GetReg(inst.Data11).ValueFromHex();
		    DataSegment = (Byte far *)MK_FP(Segment,0);
		    break;
      case CBW  : Regs[REG_AX] = /*toString("(int)") + */GetReg(REG_AL);
		  SetRegUsed(REG_AL);
		  break;
      case CWD  : /*if (strncmp((char *)GetReg(REG_AX),"(int)",5)==0)
		    strcpy((char *)GetReg(REG_AX),((char *)GetReg(REG_AX))+5);
		  */tmp = GetReg(REG_AX);
		  //Regs[REG_AL] = toString("LOBYTE((long)") + tmp + ")";
		  Regs[REG_AX] = tmp;
		  Regs[REG_DX] = toString("HIWORD((long)") + tmp + ")";
		  SetRegUsed(REG_AX);
		  break;
      case LEA  : tmp=GetOperand2(inst);
		  Regs[inst.Data11] =  toString("&") + tmp;
		  if (RegisterVariable(inst.Data11))
		  {
		    sprintf(LineBuffer,"%s = _%s;",Regstr[i],GetRegVarName(inst.Data11));
		    CheckAndOutput(LineBuffer,How,sList);
		  }
		  else SetRegModified(inst.Data11);
		  break;
      case IDIV : case DIV :
		  switch(inst.Operand1)
		  {
		    case REG_DIRECT :
		    case MEMORY :
		    case REG_INDIRECT :
		    case INDEXED_BYTE :
		    case INDEXED_WORD :
			CheckAndSetRegUsed(inst.Operand1,inst.Data11);
			SetRegModified(REG_AL);
			SetRegModified(REG_AX);
			SetRegModified(REG_DX);

			tmp=GetOperand1(inst);
			if (strncmp(GetReg(REG_AX),"(long)",6)==0)
			  strcpy((char *)GetReg(REG_AX),((char *)GetReg(REG_AX))+6);
			if (inst.operSize1==0)
			{
			  Regs[REG_AL] = toString("(") + GetReg(REG_AX) + \
			  ") / (" + tmp + ")";
			}
			else
			{
			  tmp1 = toString("(") + GetReg(REG_AX) + ") / (" + \
				tmp + ")";
			  tmp = toString("(") + GetReg(REG_AX) + ") % (" + \
				tmp + ")";
			  Regs[REG_AL] = tmp1;
			  Regs[REG_AX] = tmp1;
			  Regs[REG_DX] = tmp;
			  Regs[REG_DL] = tmp;
			}
			break;
		  }
		  break;
      case IMUL : case MUL :
		  switch(inst.Operand1)
		  {
		    case REG_DIRECT :
		    case MEMORY :
		    case REG_INDIRECT :
		    case INDEXED_BYTE :
		    case INDEXED_WORD :
			CheckAndSetRegUsed(inst.Operand1,inst.Data11);
			SetRegModified(REG_AL);
			SetRegModified(REG_AX);
			SetRegModified(REG_DX);

			tmp=GetOperand1(inst);
			if (inst.operSize1==0)
			{
			  Regs[REG_AX] = toString("(") + GetReg(REG_AL) + \
			  ") * (" + tmp + ")";
			}
			else
			{
			  tmp = toString("(") + GetReg(REG_AX) + ") * (" + \
				tmp + ")";
			  Regs[REG_AL] = tmp;
			  Regs[REG_AX] = tmp;
			  Regs[REG_DX] = toString("HIWORD(") + tmp + ")";
			}
			break;
		  }
		  break;
      case NOT  : case NEG :
		  switch(inst.Operand1)
		  {
		    case MEMORY :
		    case REG_INDIRECT :
		    case INDEXED_BYTE :
		    case INDEXED_WORD :
			tmp = GetOperand1(inst);
		      ComPlaceForNot:
			sprintf(EndOf(LineBuffer),"%s = %c (%s);",(char *)tmp,\
				(inst.Instr==NOT)?'~':'-',\
				(char *)tmp);
			CheckAndOutput(LineBuffer,How,sList);
			break;
		    case REG_DIRECT :
			if (RegisterVariable(inst.Data11))
			{
			  tmp=GetRegVarName(inst.Data11);
			  goto ComPlaceForNot;
			}
			else
			{
			  Regs[inst.Data11] = toString((inst.Instr==NOT) ? \
				"~ (":"- (") + GetReg(inst.Data11) + ")";
			  SetRegModified(inst.Data11);
			  SetRegUsed(inst.Data11);
			}
			break;
		  }
		  break;
      case CMP  : inst.GetVariable(v1,1);
		  if (IsLongInt(v1) && !IsFirstWordOfLongInt(v1)) break;
		  inst.GetVariable(v1,2);
		  if (IsLongInt(v1) && !IsFirstWordOfLongInt(v1)) break;

		  if (inst.Operand1==REG_DIRECT)
		    CheckAndDeletePending(GetReg(inst.Data11));
		  CheckAndSetRegUsed(inst.Operand1,inst.Data11);
		  CheckAndSetRegUsed(inst.Operand2,inst.Data21);
		  // if we are just checking the registers modified in this
		  // part of the code, don't decode the switch statement.
		  if (!RegModified && !RegUsed)
		  {
		    if (InSwitchStatement(SwInfo))
		    {
		      if (inst.Operand1!=REG_DIRECT)
		      {
			sprintf(errorStr,"Error in decoding a Switch construct at %X",InstAddr);
			ErrorList.Add(errorStr);
			break;
		      }
		      if (IsSwitchStatement(InstAddr)==C_SWITCH_ORDERED)
		      {
			if (inst.Operand2==IMMEDIATE) tmp=GetOperand1(inst);
			else tmp=GetOperand1(inst);
		      }
		      else
		      {
			if ((inst.Operand2==INDEXED_BYTE ||
			     inst.Operand2==INDEXED_WORD) &&
			     inst.Data21==INDX_BP) tmp=GetOperand2(inst);
			else tmp=GetOperand1(inst);
		      }
		      sprintf(LineBuffer,"switch(%s)",(char *)tmp);
		      CheckAndOutput(LineBuffer,How,sList);
		      CheckAndOutput("{",How,sList);

		      // skip all code upto the end of the switch statement.
		      Byte far *tInst = DecodeSwitchStatement(SwInfo,How,sList,FP_OFF(BlockBegin)+BlockSize);
		      // make adjustments to the counter variables.
		      ctr += tInst-Inst;
		      Inst = tInst;
		      // Just to fool the incrementing code at the end of
		      //  the switch.
		      ctr -= InstLen;
		      Inst -= InstLen;

		      CheckAndOutput("}",How,sList);
		      break;
		    }
		  }
		  CompareVar.Operand1=GetOperand1(inst);
		  CompareVar.Operand2=GetOperand2(inst);
		  break;
      case JMP  : /*if(IsReturnStatement())
		  {
		    tmp = "return ";
		    if (NotUsed[REG_DX])
		    {
		      CheckAndDeletePending(Regs[REG_DX]);
		      // if DX = HIWORD(some expression), ignore it.
		      if (strncmp(Regs[REG_DX],"HIWORD",6)!=0)
			tmp=Regs[REG_DX];
		    }
		    if (!NotUsed[REG_DX] && NotUsed[REG_AX])
		    {
		      CheckAndDeletePending(Regs[REG_AX]);
		      tmp+=Regs[REG_AX];
		    }
		    tmp += ";";
		    sprintf(LineBuffer,"%s",(char *)tmp);
		  }
		  else*/
		  {
		    GetLabel(inst.Data11,tmp);
		    sprintf(LineBuffer,"goto %s;",(char *)tmp);
		  }
		  CheckAndOutput(LineBuffer,How,sList);
		  break;
//	JA, JAE, JB, JBE, JZ, JNZ, JG, JGE, JL, JLE
//	Others normally do not occur in C Programs. Hence ignored.
      case JA : case JAE : case JB : case JBE :
		  i=1; goto comjmp;
      case JL : case JG : case JZ :
      case JLE: case JGE: case JNZ:
		  i=0;
		comjmp:
		  // No CMP instruction encountered before?
		  if (((char *)CompareVar.Operand1)[0]==0)
		  {
		    //printf("\nUnknown Code Structure at %X\n",InstAddr);
		    break;
		  }
		  sprintf(LineBuffer,"if(");
		  //if (i) sprintf(EndOf(LineBuffer),"(unsigned)");
		  sprintf(EndOf(LineBuffer),"(%s)",\
			  (char *)CompareVar.Operand1);
		  sprintf(EndOf(LineBuffer)," %s ",\
			  CompOperators[inst.Instr - JA]);
		  //if (i) sprintf(EndOf(LineBuffer),"(unsigned)");
		  CompareVar.EndAddress = inst.Data11;
		  GetLabel(inst.Data11,tmp);
		  sprintf(EndOf(LineBuffer),"(%s))",(char *)CompareVar.Operand2);
		  Byte far *p=Inst+InstLen;
		  // If we are just checking the registers modified in this
		  // part of the code, don't decode ternary operators etc.
		  if (!RegUsed && !RegModified)
		  // else, Check for ternary operator code here.
		    p = CheckForTernaryOperCode(Inst+InstLen,LineBuffer+2);
		  if(p!=Inst+InstLen)
		  {
		    ctr += p-Inst;
		    Inst = p;
		    // Just to fool the incrementing code at the end of the proc.
		    InstLen=0;
		  }
		  else
		  {
		    sprintf(EndOf(LineBuffer)," goto %s;",(char *)tmp);
		    CheckAndOutput(LineBuffer,How,sList);
		  }
		  ((char *)CompareVar.Operand1)[0]=0;	// reset.
		  break;
      case SHL : case SHR :
		  if (inst.Operand2==IMMEDIATE)
		    tmp1 += inst.Data21;
		  else if (inst.Operand2==REG_DIRECT)
		    tmp1 += GetReg(inst.Data21);
		  else assert(0);
      case INC : case DEC :
		  switch(inst.Operand1)
		  {
		    case REG_DIRECT :
			// If the register used is not a register variable...
			if (!RegisterVariable(inst.Data11))
			{
			  char *t;
			  switch(inst.Instr)
			  {
			    case INC : t="+1"; break;
			    case DEC : t="-1"; break;
			    case SHL : t="<<"; break;
			    case SHR : t=">>"; break;
			  }
			  Regs[inst.Data11] = toString("(") + \
				GetReg(inst.Data11) + t + tmp1 + ")";
			  SetRegModified(inst.Data11);
			  SetRegUsed(inst.Data11);
			  break;
			}
			// If it is a register variable, or other operands,...
		    default :
			tmp=GetOperand1(inst);
			char *t;
			switch(inst.Instr)
			{
			  case INC : t="++"; break;
			  case DEC : t="--"; break;
			  case SHL : t="<<="; break;
			  case SHR : t=">>="; break;
			}
			sprintf(LineBuffer,"%s%s%s;",(char *)tmp,\
				t,(char *)tmp1);
			CheckAndOutput(LineBuffer,How,sList);
			break;
		  }
		  break;
      case ADC  : case SBB :
		  // only the high words of the long-int are involved in
		  // ADC & SBB. Hence subtract 2 from the variable to get
		  // the actual address of the long int.
		  int whichOper;
		  whichOper=(inst.Operand1!=REG_DIRECT)?1:2;
		  if (whichOper==1) inst.GetVariable(v1,1);
		  else inst.GetVariable(v1,2);

		  DelVar(v1-2); DelVar(v1);
		  AddVar(v1-2,VAR_LONG,4);
		  if (inst.Instr==ADC) inst.Instr=ADD;
		  else inst.Instr=SUB;
		  goto ComForMovingData;
      // We get an 'LES' instruction while using 'far' pointers.
      // eg.  LES BX,[1000] means we are loading es:bx with far-pointer
      // located at 1000.  Here, I just ignore the 'es' part of the
      // instruction, and load BX with [1000].
      case LES  : inst.Instr=MOV;
      case MOV  : case ADD : case SUB :
      case AND  : case OR  : case XOR :
		  // Check if the destination operand is a part of a long-int.
		  //  Execute the instruction only if it is the HIWORD of the
		  //  long-int.
		ComForMovingData:
		  int Use2ndData=0;
		  int HiWordPrefix=0;
		  whichOper=(inst.Operand1!=REG_DIRECT)?1:2;

		  if (whichOper==1) inst.GetVariable(v1,1);
		  else inst.GetVariable(v1,2);

		  if (IsLongInt(v1))
		  {
		    // If it is not the 1st word of the long-int, ignore
		    //  the instruction.
		    if (!IsFirstWordOfLongInt(v1))
		    {
		      if (inst.Operand2==IMMEDIATE)
		      {
			PrevImmValueStored=1;
			PrevImmediateValue=inst.Data21;
		      }
		      if (whichOper==2)
		      {
			inst.SetVariable(v1-2,2);
			HiWordPrefix=1;
		      }
		      else break;
		    }
		    if (inst.Operand2==IMMEDIATE)
		    {
		      // This instruction operates on the 1st word using
		      //  an immediate value. Grab the 2nd word of the
		      //  immediate value - which could be the previous or
		      //  next instruction.
		      // If it has not been obtained from previous
		      //  instruction it must be in the next instruction.
		      if (!PrevImmValueStored)
		      {
			Inst+=InstLen;
			d.TraceInst(Inst,InstLen);
			if (d.CurInst.Instr==ADC) d.CurInst.Instr=ADD;
			if (d.CurInst.Instr==SBB) d.CurInst.Instr=SUB;
			if(d.CurInst.Instr!=inst.Instr ||
			   d.CurInst.Operand2!=IMMEDIATE)
			{
			  sprintf(errorStr,"Error understanding long int code at %X",InstAddr);
			  ErrorList.Add(errorStr);
			  break;
			}
			PrevImmediateValue=d.CurInst.Data21;
		      }
		      inst.Data22=inst.Data21;
		      inst.Data21=PrevImmediateValue;
		      PrevImmValueStored=0;
		      Use2ndData=1;
		    }
		  }
		  switch(inst.Operand1)
		  {
		    case REG_INDIRECT :
		    case INDEXED_BYTE :
		    case INDEXED_WORD :
			tmp=GetOperand1(inst);
			goto PrintVariableAssignment;
		    case MEMORY :
			tmp=GetOperand1(inst);
		      PrintVariableAssignment:
			tmp1=tmp;
			sprintf(LineBuffer,"%s %s= ",(char *)tmp,\
				ArithOperators[inst.Instr - MOV]);
			switch(inst.Operand2)
			{
			  case IMMEDIATE :
				s_LineBuffer += (Word)inst.Data21;
				if (Use2ndData)		// See above.
				  s_LineBuffer += (Word)inst.Data22;
				break;
			  case REG_DIRECT :
				CheckAndSetRegUsed(inst.Operand2,inst.Data21);
				tmp=GetOperand2(inst);
				strcat(LineBuffer,(char *)tmp);
				// Is it a long-int?
				if (strncmp((char *)tmp,"HIWORD",6)==0)
				  Use2ndData=1;	// Used in the checking below.
				// If it is like MOV addr,REG then do a false
				//  assignment MOV REG,addr so that any
				//  following instructions of the format
				//  MOV addr,REG are translated properly.
				if (!RegisterVariable(inst.Data21))
				{
				  tmp=GetOperand1(inst);
				  Regs[inst.Data21] = tmp;
				}
				break;
			  default :
				tmp = GetOperand2(inst);
				strcat(LineBuffer,(char *)tmp);
				break;
			}
			sprintf(EndOf(LineBuffer),";");
			CheckAndOutput(LineBuffer,How,sList);
			// check for long-ints and register them
			if (Use2ndData)
			{
			  inst.GetVariable(v1,1);
			  if (!IsLongInt(v1))
			  {
			    DelVar(v1-2); DelVar(v1);
			    AddVar(v1-2,VAR_LONG,4);
			  }
			}
			break;
		    case REG_DIRECT :
			tmp=GetOperand2(inst);
			tmp1=tmp;
			if (HiWordPrefix) tmp=toString("HIWORD(")+tmp+")";
			SetRegModified(inst.Data11);
			CheckAndSetRegUsed(inst.Operand2,inst.Data21);
			// Is it ADD SP,Imm?
			if ((inst.Instr == ADD) && \
			    (inst.Operand2 == IMMEDIATE) && \
			    (inst.Data11 == Register(1,4)))
			{
			  int dec = (int)inst.Data21 / sizeof(Word);
			  for(i=0;i<dec;i++,StackTop--)
			    Stack[StackTop-1] = "";
			  assert(StackTop>=0);		// Stack Underflow?
			  break;
			}
			if (inst.Instr == MOV)
			  Regs[inst.Data11] = tmp;
			else
			{
			  tmp = toString("(") + tmp + ")";
			  Regs[inst.Data11] = GetReg(inst.Data11) +  \
			    " " + ArithOperators[inst.Instr - MOV] \
			    + " " + tmp;
			}
			// Check if it is a register variable
			// If so,  print it out the same way a memory
			//  variable is assigned.
			if (RegisterVariable(inst.Data11))
			{
			  tmp=GetRegVarName(inst.Data11);
			  goto PrintVariableAssignment;
			}
			SetRegModified(inst.Data11);
			break;
		  }
		  break;
      case PUSH : i = StackTop; assert(StackTop<NUM_STACK_LINES);
		  CheckAndSetRegUsed(inst.Operand1,inst.Data11);
		  tmp=GetOperand1(inst);
		  Stack[i] += tmp;
		  UseAsFnParms[i]=1;
		  // Check for PUSHs involving part of long-ints.
		  inst.GetVariable(v1,1);
		  if (IsLongInt(v1))
		    UseAsFnParms[i]=IsFirstWordOfLongInt(v1);

		  // If it is a comma-expression, close the braces.
		  if (!IsEqualClosingParentheses(Stack[i])) Stack[i] += ")";
		  // Clear the current stack string to blank.
		  StackTop++;
		  Stack[StackTop] = "";
		  TracingFunctionCall=1;
		  break;
      case POP  : if (StackTop<=0)
		  {
		    sprintf(errorStr,"Procedure Stack Underflow at %X",InstAddr);
		    ErrorList.Add(errorStr);
		    break;
		  }
		  StackTop--;
		  switch(inst.Operand1)
		  {
		    case MEMORY :
		    case INDEXED_BYTE :
		    case INDEXED_WORD :
		      tmp = GetOperand1(inst);
		      sprintf(EndOf(LineBuffer),"%s = %s;",(char *)tmp,\
			      (char *)Stack[StackTop]);
		      CheckAndOutput(LineBuffer,How,sList);
		      break;
		    case REG_DIRECT :
		      Regs[inst.Data11] = Stack[StackTop];
		      SetRegModified(inst.Data11);
		      break;
		  }
		  Stack[StackTop] = "";
		  TracingFunctionCall=0;
		  break;
      case CALL : TracingFunctionCall=0;
		  if (IsInternalLongIntProc(inst))
		    break;
		  inst.GetVariable(v1,1);
		  vName=GlobalVars.GetName(v1,VAR_LIB_FUNCTION);
		  if (!vName) vName=GlobalVars.GetName(v1,VAR_USER_FUNCTION);
		  if (vName) tmp=vName;
		  else
		  {
		    if (inst.operSize1) tmp = String((Word)inst.Data12);
		    else tmp="";
		    tmp += String(inst.Data11);
		  }
		  sprintf(EndOf(LineBuffer),"%s(",(char *)tmp);
		  // For some library procedures, no parameters are passed
		  //  by stack, hence ignore the stack in such cases.  This
		  //  is checked by finding if the previous instruction
		  //  was a PUSH - if not, just ignore the stack.
		  if (previnst.Instr==PUSH && StackTop)
		  {
		    for(i=StackTop-1;i>=0;i--) if (UseAsFnParms[i]) break;
		    if (i>=0)
		    {
		      sprintf(EndOf(LineBuffer),"%s",(char *)Stack[i]);
		      i--;
		      for(;i>=0;i--)
			if (UseAsFnParms[i])
			  sprintf(EndOf(LineBuffer),",%s",(char *)Stack[i]);
		    }
		  }
		  // Append the address of the instruction 'CALL Proc' to
		  //  the pending line buffer, so that this address tag can
		  //  be used to locate this statement in case it has to be
		  //  deleted from the pending lines array.
		  // Don't terminate LineBuffer with a ';' because pending
		  //  lines should not be terminated now, only while printing.
		  sprintf(EndOf(LineBuffer),")");
		  tmp = toString(";") + String((int)InstAddr) + ";";
		  Regs[REG_AL] = tmp + LineBuffer;
		  Regs[REG_AX] = tmp + LineBuffer;
		  Regs[REG_DX] = tmp + toString("HIWORD(") + LineBuffer + ")";
		  SetRegModified2(REG_AL);
		  SetRegModified2(REG_AX);
		  SetRegModified2(REG_DX);
		  String tmp1 = tmp + LineBuffer;
		  strcpy(LineBuffer,(char *)tmp1);
		  CheckAndAddPending(LineBuffer);

		  // Some functions give a RET/RETF xxxx while returning
		  //  so that there is no need to do a stack unwind by the
		  //  caller.  Check for such conditions, and unwind the
		  //  stack by a suitable amount.
		  Word seg,off;
		  if (inst.operSize1==1) { seg=inst.Data11; off=inst.Data12; }
		  else { seg=FP_SEG(Inst); off=inst.Data11; }
		  Byte far *ProcBegin = (Byte far *)MK_FP(seg,off);
		  Byte far *ProcEnd = ProcBegin+GetProcSize(ProcBegin);
		  Disasm d;
		  d.TraceInst(ProcEnd,i);
		  int dec = (int)d.CurInst.Data11 / sizeof(Word);
		  for(i=0;i<dec;i++,StackTop--)
		    Stack[StackTop-1] = "";
		  assert(StackTop>=0);		// Stack Underflow?
		  break;
      case LOOP : break;
      default   : sprintf(errorStr,"Unknown Instruction at address %X",InstAddr);
		  ErrorList.Add(errorStr);
		  break;
    }
    Inst += InstLen;
    ctr += InstLen;
    previnst=inst;
  }
  CheckAndOutput(NULL,How,sList);
  InstAddr = FP_OFF(Inst);
  if (GetLabel(InstAddr,tmp))
  {
    tmp += ":";
    CheckAndOutput(tmp,How,sList);
  }
}
